#pragma once

// __s 和 _S 是不允许用户定义的
// 两个下划线加小写和一个下划线加大写，是不允许用户定义的
// 因此一般而言，标准库的实现会在内部使用这种命名方式，如 _Tp

#if defined(__GNUC__) || defined(__clang__)
#define UNREACHABLE() __builtin_unreachable()
#elif defined(_MSC_VER)
#define UNREACHABLE() __assume(0)
#else
#define UNREACHABLE() std::abort()
#endif

template <typename _Tp, size_t _N>
class Array
{
private:
    _Tp m_elements[_N];

public:
    using value_type = _Tp;
    using size_type = size_t;
    using iterator = _Tp *;
    using const_iterator = _Tp const *;

    Array() {}
    Array(std::initializer_list<_Tp> const &list)
    {
        int count = 0;
        for (auto &element : list)
        {
            m_elements[count] = element;
            ++count;
        }
    }
    Array(Array const &other)
    {
        for (size_t __i = 0; __i < _N; ++__i)
        {
            m_elements[__i] = other.m_elements[__i];
        }
    }
    Array(Array &&other)
    {
        for (size_t __i = 0; __i < _N; ++__i)
        {
            m_elements[__i] = std::move(other.m_elements[__i]);
        }
    }
    Array &operator=(Array const &other)
    {
        for (size_t __i = 0; __i < _N; ++__i)
        {
            m_elements[__i] = other.m_elements[__i];
        }
        return *this;
    }
    Array &operator=(Array &&other)
    {
        for (size_t __i = 0; __i < _N; ++__i)
        {
            m_elements[__i] = std::move(other.m_elements[__i]);
        }
        return *this;
    }
    virtual ~Array() {}

    _Tp &operator[](size_t __index) noexcept
    {
        return m_elements[__index];
    }

    _Tp const &operator[](size_t __index) const noexcept
    {
        return m_elements[__index];
    }

    _Tp &at(size_t __index)
    {
        if (__index >= _N) [[unlikely]]
            throw std::runtime_error("Index out of range, __index = " + std::to_string(__index) + ", N = " + std::to_string(_N) + "\n");
        return m_elements[__index];
    }

    _Tp const &at(size_t __index) const
    {
        if (__index >= _N) [[unlikely]]
            throw std::runtime_error("Index out of range, __index = " + std::to_string(__index) + ", N = " + std::to_string(_N) + "\n");
        return m_elements[__index];
    }

    static constexpr size_t size()
    {
        return _N;
    }

    static constexpr size_t max_size()
    {
        return 0;
    }

    _Tp &front() noexcept
    {
        return m_elements[0];
    }

    _Tp const &front() const noexcept
    {
        return m_elements[0];
    }

    _Tp &back() noexcept
    {
        return m_elements[_N - 1];
    }

    _Tp const &back() const noexcept
    {
        return m_elements[_N - 1];
    }

    void fill(_Tp const &__value) noexcept(noexcept(std::declval<_Tp &>() = std::declval<_Tp const &>()))
    {
        for (size_t __i = 0; __i < _N; ++__i)
        {
            m_elements[__i] = __value;
        }
    }

    _Tp *data() noexcept
    {
        return m_elements;
    }

    _Tp const *data() const noexcept
    {
        return m_elements;
    }

    _Tp *begin()
    {
        return m_elements;
    }
    _Tp *end()
    {
        return m_elements + _N;
    }
    _Tp const *begin() const
    {
        return m_elements;
    }
    _Tp const *end() const
    {
        return m_elements + _N;
    }
};

template <typename _Tp>
class Array<_Tp, 0>
{
public:
    Array() = delete;
    Array(std::initializer_list<_Tp> const &list) = delete;
    virtual ~Array() = delete;

    _Tp &operator[](size_t __index) noexcept
    {
        // UNREACHABLE();  // since C++23
        UNREACHABLE();
    }

    _Tp const &operator[](size_t __index) const noexcept
    {
        UNREACHABLE();
    }

    _Tp &at(size_t __index)
    {
        UNREACHABLE();
    }

    _Tp const &at(size_t __index) const
    {
        UNREACHABLE();
    }

    static constexpr size_t size()
    {
        return 0;
    }

    static constexpr size_t max_size()
    {
        return 0;
    }

    _Tp &front() noexcept
    {
        UNREACHABLE();
    }

    _Tp const &front() const noexcept
    {
        UNREACHABLE();
    }

    _Tp &back() noexcept
    {
        UNREACHABLE();
    }

    _Tp const &back() const noexcept
    {
        UNREACHABLE();
    }

    void fill(_Tp const &value)
    {
    }

    _Tp *data() noexcept
    {
        return nullptr;
    }

    _Tp *begin()
    {
        return nullptr;
    }

    _Tp *end()
    {
        return nullptr;
    }

    _Tp const *begin() const
    {
        return nullptr;
    }

    _Tp const *end() const
    {
        return nullptr;
    }
};

template <typename T, typename ..._Ts>
Array(T, _Ts...) -> Array<T, 1 + sizeof...(_Ts)>;


// C风格数组转为Array
template <typename _Tp, size_t _N>
Array<_Tp, _N> make_array(_Tp (&__array)[_N]) noexcept
{
    Array<_Tp, _N> __result;
    for (size_t __i = 0; __i < _N; ++__i)
    {
        __result[__i] = __array[__i];
    }
    return __result;
}